#pragma once

#include <winsock2.h>
#include <Iphlpapi.h>

#pragma comment(lib,"WS2_32.lib")
#pragma comment (lib, "Iphlpapi.lib")

#include <string>

namespace gtl
{

namespace net
{

class socket
{
public:
	socket(void);
	socket(SOCKET sock);
	socket(const socket& sock);
	virtual ~socket(void);

protected:
	SOCKET m_socket;
	struct sockaddr_in m_recvsockaddr;
	struct sockaddr_in m_destsockaddr;
	std::string m_straddr; // local host address or remote host address

public:
	// the visit interface
	template<class RetType, class Visitor>
		RetType visit(Visitor& visitor) 
	{
		return visitor.operator()<RetType>(*this);
	}

public:
	static bool init(void);
	static int  clean(void);

	// create a socket, if port not zero, then call bind function.
	int			create(unsigned int port = 0, int type = SOCK_STREAM, const char* address = NULL, int af = AF_INET);
	int			connect(const char* address, unsigned short port);
	int			connect(unsigned long ip, unsigned short port);
	int			setsockopt(int level, int optname, const char* optval, int optlen);
	int			shutdown(int how);
	int			closesocket(void);

	// tcp
	int			listen(int backlog);
	SOCKET		accept(void);
	virtual int	send(const char* buf, int len, int flags = 0);
	virtual int	recv(char* buf, int len, int flags = 0);

	// udp 
	virtual int sendto(const char* buf, int len, const char* ip, unsigned short port, int flags = 0);
	virtual int sendto(const char* buf, int len, const sockaddr* to, int flags = 0);
	virtual int sendto(const char* buf, int len, int flags = 0);

	virtual int recvfrom(char* buf, int len, char ip[16], unsigned short& port, int flags = 0);
	virtual int recvfrom(char* buf, int len, sockaddr* from, int flags = 0);
	virtual int recvfrom(char* buf, int len, int flags = 0);

public:
	int			ioctl(long cmd, unsigned long opt);
	void		setsocket(SOCKET socket);
	void		setsocket(socket& sock);
	SOCKET		getsocket(void) const;

	operator SOCKET();
	socket&	operator =(const socket& sock);

public:
	// extend function
	void		makedestsockaddr(const char* ipaddr, unsigned int port, short family = AF_INET, const char* sin_zero = NULL);//if ipaddr is null, bind ip to INADDR_ANY
	const char* getsockaddr(bool brecvaddr = true);
	unsigned int getsockport(bool brecvaddr = true);
	int			getremoteport(void);
	int			getlocalport(void);
	const char* getremoteaddr(void);
	const char* getlocaladdr(void);

public:
	template<class String>
	static bool get_local_ip(String& ip);

	template<class String>
	static bool get_mac_addr(String& mac);

	static bool dnsparse(const char* domain, char* ip);
};

#include "socket.inl"

} // end namespace net

} // end namespace gtl